package function.encrypt;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * 加密算法
 */
public class TestEncrypt {


    public static void main(String[] args) throws Exception {
        // 严格说不算加密，只要jdk都能解密
        base64();

        // 摘要算法
        md();
        sha();

        // 对称加密
        des();

        // 对称加密
        // https 使用的加密算法
        aes();

        // 对称加密 + 盐
        pbe();

        // 非对称加密（公钥 + 私钥）
        // https 使用的加密算法
        rsa();
    }


    public static void base64() throws NoSuchAlgorithmException {

        // 原始数据
        String str = "Hello World";

        // 加密
        Base64.Encoder encoder = Base64.getEncoder();
        String encoderStr = encoder.encodeToString(str.getBytes(StandardCharsets.UTF_8));
        System.out.println("encoder: " + encoderStr);

        // 解密
        Base64.Decoder decoder = Base64.getDecoder();
        String decoderStr = new String(decoder.decode(encoderStr), StandardCharsets.UTF_8);
        System.out.println("decoder: " + decoderStr);
    }

    public static void md() throws NoSuchAlgorithmException {

        // 原始字符
        String data = "Hello World!";

        // 编码
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(data.getBytes());

        // 转化为16进制编码
        BigInteger bigInteger = new BigInteger(1, digest);
        System.out.println("JDK MD5: " + bigInteger.toString(16));
    }

    public static void sha() throws NoSuchAlgorithmException {
        // 原始字符
        String data = "Hello World!";

        // SHA-1 编码
        MessageDigest md = MessageDigest.getInstance("SHA");
        byte[] digest = md.digest(data.getBytes());

        BigInteger bigInteger = new BigInteger(1, digest);
        System.out.println("JDK SHA: " + bigInteger.toString(16));
    }

    public static void des() throws Exception {
        // 原始字符
        String data = "Hello World!";

        // 生成key
        KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
        keyGenerator.init(56);      //指定key长度，同时也是密钥长度(56位)
        SecretKey secretKey = keyGenerator.generateKey(); //生成key的材料
        byte[] key = secretKey.getEncoded();  // 生成key

        // key转换成密钥
        DESKeySpec desKeySpec = new DESKeySpec(key);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
        SecretKey key2 = factory.generateSecret(desKeySpec); // 生成秘钥

        // 加密
        Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");  //算法类型/工作方式/填充方式
        cipher.init(Cipher.ENCRYPT_MODE, key2);   // 指定为加密
        byte[] result1 = cipher.doFinal(data.getBytes()); // 加密
        BigInteger bigInteger = new BigInteger(1, result1);
        System.out.println("jdkDES加密: " + bigInteger.toString(16));  // 转换为十六进制
        System.out.println("jdkDES加密: " + new String(result1)); // 转换字符串

        // 解密
        cipher.init(Cipher.DECRYPT_MODE, key2);  // 指定为解密
        byte[] result2 = cipher.doFinal(result1);   // 解密
        System.out.println("jdkDES解密: " + new String(result2));  // 转换字符串
    }

    public static void aes() throws Exception {

    }

    public static void pbe() throws Exception {
        // 原始字符
        String data = "Hello World!";

        // 生成key
        String password = "TEST"; // 口令
        PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());

        // key转换成密钥
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWITHMD5andDES");
        Key key = factory.generateSecret(pbeKeySpec);

        // 初始化盐
        SecureRandom random = new SecureRandom();
        byte[] salt = random.generateSeed(8);   // 指定为8位的盐 （盐就是干扰码，通过添加干扰码增加安全）

        // 加密
        PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(salt, 100);   // 参数规范，第一个参数是盐，第二个是迭代次数（经过散列函数多次迭代）
        Cipher cipher = Cipher.getInstance("PBEWITHMD5andDES");
        cipher.init(Cipher.ENCRYPT_MODE, key, pbeParameterSpec);
        byte[] result1 = cipher.doFinal(data.getBytes());
        System.out.println("jdk PBE加密: " + Base64.getEncoder().encodeToString(result1));
        System.out.println("jdk PBE加密: " + new String(result1));

        // 解密
        cipher.init(Cipher.DECRYPT_MODE, key, pbeParameterSpec);
        byte[] result2 = cipher.doFinal(result1);
        System.out.println("jdk PBE解密: " + new String(result2));
    }


    public static void rsa() throws Exception {
        // 原始字符
        String data = "Hello World!";

        // 使用同一个key生成公钥和私钥
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(512);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();           // 获取公钥
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();       // 获取私钥
        System.out.println("public key:" + Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded()));
        System.out.println("private key:" + Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded()));

        // 私钥加密，公钥解密--加密
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] result1 = cipher.doFinal(data.getBytes()); // 使用私钥加密
        System.out.println("RSA私钥加密，公钥解密--加密:" + Base64.getEncoder().encodeToString(result1));

        // 私钥加密，公钥解密--解密
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
        keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] result2 = cipher.doFinal(result1); // 使用公钥解密
        System.out.println("RSA私钥加密，公钥解密--解密:" + new String(result2));

        // 公钥加密，私钥解密--加密
        x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
        keyFactory = KeyFactory.getInstance("RSA");
        publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] result3 = cipher.doFinal(data.getBytes());
        System.out.println("RSA公钥加密，私钥解密--加密:" + Base64.getEncoder().encodeToString(result3));

        // 公钥加密，私钥解密--解密
        pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
        keyFactory = KeyFactory.getInstance("RSA");
        privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] result4 = cipher.doFinal(result3);
        System.out.println("RSA公钥加密，私钥解密--解密:" + new String(result4));
    }
}
